package com.zingrow.web.base.service.impl;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import org.apache.log4j.Logger;

import com.zingrow.common.utils.CommonUtils;
import com.zingrow.web.base.CodeMsg;
import com.zingrow.web.base.controller.BaseController;
import com.zingrow.web.base.service.IBaseService;
import com.zingrow.web.common.exception.AjaxBusinessException;
import com.zingrow.web.common.exception.BusinessException;

/**
 * 基础服务，只包含最基本的 增、删、改、查 <br>
 * <p>
 * 注：<br>
 * 1、必须用MyBatis Generator反向生成 mapper<br>
 * 2、生成的 mapper 方法名称不能改动 <br>
 * <p>
 * 使用：<br>
 * 1、继承 BaseService <br>
 * 2、实现initMapper，把对应的mapper赋值到baseMapper <br>
 * 3、使用增、删、改、查方法<br>
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public abstract class BaseService<T> implements IBaseService<T> {
    protected final Logger log = Logger.getLogger(getClass());

    protected Object baseMapper;

    private static final String InsertMet = "insert";
    private static final String SelectByIdMet = "selectByPrimaryKey";
    private static final String SelectAllMet = "selectAll";
    private static final String UpdateMet = "updateByPrimaryKey";
    private static final String DeleteMet = "deleteByPrimaryKey";

    /**
     * 把对应的mapper赋值到baseMapper。
     */
    public abstract void initMapper();

    /**
     * 校验新增、修改的时候，model数据的完整性。
     * 
     * @param t
     *            模型对象。
     */
    public void checkSaveOrUpdateData(T t) {

    }

    /**
     * 新增。
     * 
     * @param t
     *            模型对象。
     * @return 返回受影响记录条数，如：成功插入1条数据，则返回1。
     */
    @Override
    public int insert(T t) {
        checkSaveOrUpdateData(t);
        return (Integer) invokeByName(InsertMet, t);
    }

    /**
     * 修改。
     * 
     * @param t
     *            模型对象。
     * @return 返回受影响记录条数，如：成功插入1条数据，则返回1。
     */
    @Override
    public int update(T t) {
        checkSaveOrUpdateData(t);
        return (Integer) invokeByName(UpdateMet, t);
    }

    /**
     * 根据主键（id）删除。
     * 
     * @param id
     *            模型对象id。
     * @return 返回受影响记录条数，如：成功插入1条数据，则返回1。
     */
    @Override
    public int delete(Integer id) {
        return (Integer) invokeByName(DeleteMet, id);
    }

    /**
     * 根据主键（id）查询。
     * 
     * @param id
     *            模型对象id。
     * @return 返回模型对象。
     */
    @Override
    public T selectById(Integer id) {
        return (T) invokeByName(SelectByIdMet, id);
    }

    /**
     * 查询所有。
     * 
     * @return 返回模型对象集合。
     */
    @Override
    public List<T> selectAll() {
        return (List<T>) invokeByName(SelectAllMet);
    }

    /**
     * 获取mapper的Class。
     */
    private Class getBaseMapperClz() {
        if (baseMapper == null) {
            initMapper();
            if (baseMapper == null)
                throw new RuntimeException("请先setMapper");
        }
        return baseMapper.getClass();
    }

    /**
     * 通过反射调用mapper对应方法。
     */
    private Object invokeByName(String methodName, Object... args) {
        Method insertMethod;
        Object rtn = null;
        try {
            Class[] paramType = null;
            if (args != null && args.length > 0) {
                paramType = new Class[args.length];
                for (int i = 0; i < args.length; i++) {
                    paramType[i] = args[i].getClass();
                }
            }
            insertMethod = getBaseMapperClz().getDeclaredMethod(methodName, paramType);
            rtn = insertMethod.invoke(baseMapper, args);
        } catch (SecurityException e) {
            log.error("", e);
            throw new BusinessException(e.getMessage());
        } catch (NoSuchMethodException e) {
            log.error("", e);
            throw new BusinessException(e.getMessage());
        } catch (IllegalArgumentException e) {
            log.error("", e);
            throw new BusinessException(e.getMessage());
        } catch (IllegalAccessException e) {
            log.error("", e);
            throw new BusinessException(e.getMessage());
        } catch (InvocationTargetException e) {
            log.error("", e);
            throw new BusinessException(e.getMessage());
        }
        return rtn;
    }

    /**
     * 根据是否ajax标示抛异常。
     * 
     * @Title: throwRuntimeExceptionByAjaxFlag
     * @author seff
     * @param isAJAX
     *            是否AJAX请求。
     * @param errorMsg
     *            异常提示信息。
     */
    public void throwRuntimeExceptionByAjaxFlag(boolean isAJAX, String errorMsg) {
        if (isAJAX)
            throw new AjaxBusinessException(errorMsg);
        else
            throw new BusinessException(errorMsg);
    }

    /**
     * 校验数据的完整性。
     * 
     * @Title: checkModelParamNotNull
     * @author seff
     * @param isAJAX
     *            是否AJAX请求。
     * @param objects
     *            被校验的对象集合。
     */
    public void checkModelParamNotNull(boolean isAJAX, Object... objects) {
        for (Object o : objects) {
            if (o instanceof String && CommonUtils.isStrNull((String) o))
                throwRuntimeExceptionByAjaxFlag(isAJAX, CodeMsg.MSG_MODEL_DATA_ERROR);
            if (o == null)
                throwRuntimeExceptionByAjaxFlag(isAJAX, CodeMsg.MSG_MODEL_DATA_ERROR);
        }
    }

    /**
     * 根据数据库中资源路径，结合web应用本地 路径、web应用网络路径获取外部能访问的url。<br>
     * 1)拼装本地路径，检查文件是否存在，不存在直接返回null；<br>
     * 2)如果文件存在，根据web应用网络路径拼装外部能访问的url。
     * 
     * @Title: getSourceUrl
     * @author seff
     * @param localBasePath
     *            web应用本地路径前缀。
     * @param webBasePath
     *            web应用网络路径前缀。
     * @param basePath
     *            对应资源前缀，比如用户头像，都放置在imgupload文件下，数据库中只是存里面之后的路径。
     * @param databaseFilePath
     *            数据库中存储的文件名。
     * @return 外部能访问的资源url，如果资源不存在则返回空。
     * @see BaseController#getSourceUrl(javax.servlet.http.HttpServletRequest,
     *      String, String)
     */
    public String getSourceUrl(String localBasePath, String webBasePath, String basePath, String databaseFilePath) {
        String sourceUrl = null;
        if (CommonUtils.isStrNotNull(databaseFilePath)
                && new File(localBasePath + basePath + "\\" + databaseFilePath).exists())
            sourceUrl = (webBasePath + basePath + "/" + databaseFilePath).replace("\\", "/");
        return sourceUrl;
    }

}
